#ifndef __M_DB_H__
#define __M_DB_H__

#include "util.hpp"
#include <mutex>
#include <cassert>

// 对数据库中的user表进行相关操作
class user_table
{
public:
    // 构造函数其实就是调用我们上面mysql_util里面实现的多个静态方法，调用mysql_create进行句柄的创建
    user_table(const std::string &host,
               const std::string &username,
               const std::string &password,
               const std::string &dbname,
               const uint16_t &port = 3306)
    {
        _mysql = mysql_util::mysql_create(host, username, password, dbname, port);
        assert(_mysql != nullptr);
    }

    // 析构函数中进行句柄的销毁
    ~user_table()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = nullptr;
    }

    // 用户注册时，进行gobang数据库新增
    bool insert(Json::Value &user)
    {
        if (user["username"].isNull() || user["password"].isNull())
        {
            DLOG("please input complete username or password!");
            return false;
        }

        // 如果用户存在，则下面的insert语句会执行错误，mysql会报错
#define INSERT_USER "insert into user values (null, '%s', password('%s'), 1000, 0, 0);"
        char sql[4096] = {0};
        sprintf(sql, INSERT_USER, user["username"].asCString(), user["password"].asCString());

        // 执行sql语句
        bool ret = mysql_util::mysql_exec(_mysql, sql);

        if (ret == false)
        {
            DLOG("insert user info failed!");
            return false;
        }

        return true;
    }

    // 登录验证 + 返回用户的详细信息(天梯分数，战斗场次， 胜利场次)
    bool login(Json::Value &user)
    {
        // 以用户名和密码共同作为查询过滤条件，查询到数据则表示用户名密码一致，没有信息则用户名密码错误
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=password('%s');"
        char sql[4096] = {0};
        MYSQL_RES *res = nullptr;
        sprintf(sql, LOGIN_USER, user["username"].asCString(), user["password"].asCString());

        {
            // 加锁查询 + 获取结果
            std::unique_lock<std::mutex> lock(_mtx);
            bool ret = mysql_util::mysql_exec(_mysql, sql);

            if (ret == false)
            {
                DLOG("user login failed!");
                return false;
            }

            // 查询成功，获取结果
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("have not user info!");
                return false;
            }
        }

        // RAII风格的锁出了作用域自动解锁
        // 获取结果集的行数
        int rowNum = mysql_num_rows(res);
        if (rowNum > 1)
        {
            DLOG("the user info is not unique: %d rows!", rowNum);
            return false;
        }

        if (rowNum < 1)
        {
            DLOG("the user info is not exists: %d rows!", rowNum);
            return false;
        }

        // 遍历结果集的每一个字段
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = std::stoi(row[0]);
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        // 释放结果集
        mysql_free_result(res);

        return true;
    }

    // 通过用户名获取用户详细信息
    bool select_by_username(const std::string &username, Json::Value &user)
    {
#define USER_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
        char sql[4096] = {0};
        MYSQL_RES *res = nullptr;
        sprintf(sql, USER_BY_NAME, username.c_str());

        {
            // 加锁查询 + 获取结果
            std::unique_lock<std::mutex> lock(_mtx);
            bool ret = mysql_util::mysql_exec(_mysql, sql);

            if (ret == false)
            {
                DLOG("user login failed!");
                return false;
            }

            // 查询成功，获取结果
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("have not user info!");
                return false;
            }
        }

        // RAII风格的锁出了作用域自动解锁
        // 获取结果集的行数
        int rowNum = mysql_num_rows(res);
        if (rowNum > 1)
        {
            DLOG("the user info is not unique: %d rows!", rowNum);
            return false;
        }

        if (rowNum < 1)
        {
            DLOG("the user info is not exists: %d rows!", rowNum);
            return false;
        }

        // 遍历结果集的每一个字段
        MYSQL_ROW row = mysql_fetch_row(res);
        user["username"] = username;
        user["id"] = std::atoi(row[0]);
        user["score"] = std::atoi(row[1]);
        user["total_count"] = std::atoi(row[2]);
        user["win_count"] = std::atoi(row[3]);

        // 释放结果集
        mysql_free_result(res);

        return true;
    }

    // 通过用户id获取用户详细信息
    bool select_by_userid(int id, Json::Value &user)
    {
#define USER_BY_ID "select username, score, total_count, win_count from user where id=%d;"
        char sql[4096] = {0};
        MYSQL_RES *res = nullptr;
        sprintf(sql, USER_BY_ID, id);

        {
            // 加锁查询 + 获取结果
            std::unique_lock<std::mutex> lock(_mtx);
            bool ret = mysql_util::mysql_exec(_mysql, sql);

            if (ret == false)
            {
                DLOG("user login failed!");
                return false;
            }

            // 查询成功，获取结果
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("have not user info!");
                return false;
            }
        }

        // RAII风格的锁出了作用域自动解锁
        // 获取结果集的行数
        int rowNum = mysql_num_rows(res);
        if (rowNum > 1)
        {
            DLOG("the user info is not unique: %d rows!", rowNum);
            return false;
        }

        if (rowNum < 1)
        {
            DLOG("the user info is not exists: %d rows!", rowNum);
            return false;
        }

        // 遍历结果集的每一个字段
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = id;
        user["username"] = row[0];
        user["score"] = std::atoi(row[1]);
        user["total_count"] = std::atoi(row[2]);
        user["win_count"] = std::atoi(row[3]);

        // 释放结果集
        mysql_free_result(res);

        return true;
    }

    // 用户胜利更新信息
    bool win(int id)
    {
#define WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%d;"
        char sql[4096] = {0};
        sprintf(sql, WIN, id);

        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update win_user info failed!");
            return false;
        }

        return true;
    }

    // 用户失败更新信息
    bool lose(int id)
    {
        // 注意这里sql语句输了分数下降，但是总局数要增加，胜场数不变
#define LOSE "update user set score=score-30, total_count=total_count+1 where id=%d;"
        char sql[4096] = {0};
        sprintf(sql, LOSE, id);

        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update win_user info failed!");
            return false;
        }

        return true;
    }

private:
    MYSQL *_mysql;   // mysql操作句柄
    std::mutex _mtx; // 互斥锁保护数据库的访问操作
};

#endif